package aor2.cpu.blockinterface;

import aor2.components.ElementIO;
import aor2.components.FlipFlopD;
import aor2.components.FlipFlopRS;
import aor2.components.FlipFlopException;
import aor2.components.Restorable;
import aor2.components.Signal;
import aor2.cpu.ClockCPU;
import aor2.cpu.blockcontrol.ConditionSignalSet;
import aor2.cpu.blockcontrol.ControlSignalSet;
import aor2.systembus.SystemBus;
import javax.swing.JOptionPane;

public class SynchronizationBlock implements ElementIO, Restorable {

	private boolean clkFstPhase;
	private boolean fcbusLocal, fcHighLocal = true;

	private static SynchronizationBlock singleton = null;
	private static SystemBus sysbus = SystemBus.getInstance();
	private BusOvertake obus = BusOvertake.getInstance();
	private FlipFlopRS rd, Drd, wr, Dwr;
	FlipFlopD ffc, Dffc;

	Signal komb1, komb2, komb3, enFFC, read, write, enrw, OEread, OEwrite;

	public static SynchronizationBlock getInstance() {
		if (null == singleton) {
			singleton = new SynchronizationBlock();
		}
		return singleton;
	}

	public Signal getEnFFC() {
		return enFFC;
	}

	public Signal getKomb1() {
		return komb1;
	}

	public Signal getKomb2() {
		return komb2;
	}

	public Signal getKomb3() {
		return komb3;
	}

	public Signal getOEread() {
		return OEread;
	}

	public FlipFlopRS getDwr() {
		return Dwr;
	}

	public void setDwr(FlipFlopRS dwr) {
		Dwr = dwr;
	}

	public FlipFlopRS getRd() {
		return rd;
	}

	public FlipFlopRS getDrd() {
		return Drd;
	}

	public FlipFlopRS getWr() {
		return wr;
	}

	public FlipFlopD getFfc() {
		return ffc;
	}

	public FlipFlopD getDffc() {
		return Dffc;
	}

	public Signal getOEwrite() {
		return OEwrite;
	}

	public boolean getfcLocal() {
		return fcbusLocal;
	}
	
	public boolean getfcHighLocal() {
		return fcHighLocal;
	}
	
	private SynchronizationBlock() {
		ControlSignalSet css = ControlSignalSet.getInstance();

		this.enrw = obus.enrw;
		this.read = css.get("read");
		this.read.addDestinationElementIO(this);
		this.write = css.get("write");
		this.write.addDestinationElementIO(this); // TODO videti jos sta sa ovim
		this.komb1 = new Signal(false);
		this.komb2 = new Signal(false);
		this.komb3 = new Signal(false);
		this.enFFC = obus.enFFC;
		this.OEread = new Signal(false);
		this.OEwrite = new Signal(false);

		this.ffc = new FlipFlopD(komb1, new Signal(false));
		this.Dffc = new FlipFlopD(ffc.getOut(), new Signal(false));
		this.rd = new FlipFlopRS(ffc.getOut(), komb2, new Signal(false));
		this.Drd = new FlipFlopRS(enFFC, rd.getOut(), new Signal(false));
		this.wr = new FlipFlopRS(ffc.getOut(), komb3, new Signal(false));
		this.Dwr = new FlipFlopRS(enFFC, wr.getOut(), new Signal(false));

		this.clkFstPhase = true;

	}

	public void in() {

	}

	public void out() {

	}

	public void firsttick() throws FlipFlopException {
		ffc.out();
		Dffc.out();
		rd.out();
		wr.out();
		Drd.out();
		Dwr.out();

		
		
		if (OEread.isActive())
			sysbus.setRdbus(rd.getOut().isActive());
		else if (!OEwrite.isActive()){
			sysbus.setRdbusHigh();
			sysbus.setAddressbusHigh();
		}
		if (OEwrite.isActive())
			sysbus.setWrbus(wr.getOut().isActive());
		else if (!OEread.isActive()){
			sysbus.setWrbusHigh();
			sysbus.setAddressbusHigh();
			sysbus.setDatabusHigh();
		}

	}

	public void secondtick() throws FlipFlopException {

            try {
		ffc.in();
		Dffc.in();
		rd.in();
		wr.in();
		Drd.in();
		Dwr.in();
            }
            catch (FlipFlopException f) {
                JOptionPane.showMessageDialog(null,
					"GRESKA!!!");
                 System.out.println(ClockCPU.getInstance().getT());
            }
	}

	public void next() {

		if (clkFstPhase) {
			try {
				firsttick();
			} catch (FlipFlopException e) {
				e.printStackTrace();
			}
		} else {
			try {
				secondtick();
				
			} catch (FlipFlopException e) {
				e.printStackTrace();
			}
		}
		clkFstPhase = !clkFstPhase;
                
		komb1.setActive(sysbus.getFcbus() && enrw.isActive()); // FIXME
		
		OEread.setActive(read.isActive() && enrw.isActive());
		OEwrite.setActive(write.isActive() && enrw.isActive());
		
		komb2.setActive(enrw.isActive() && read.isActive()
				&& !Drd.getOut().isActive());
		komb3.setActive(enrw.isActive() && write.isActive()
				&& !Dwr.getOut().isActive());
		enFFC.setActive(Dffc.getOut().isActive() && !ffc.getOut().isActive());
		
		ConditionSignalSet.getInstance().get("run").setActive(enFFC.isActive() || !(read.isActive() || write.isActive()));
		fcbusLocal = sysbus.getFcbus();
		fcHighLocal = sysbus.getFcbusHigh();
	}

	public void restore(String restorePoint) {
		if (singleton == null)
			singleton = new SynchronizationBlock();
		String trim = restorePoint.replace(beginTag(), "");
		trim = trim.replace(endTag(), "");
		trim = trim.trim();
		String[] states = trim.split(" ");

		komb1.clearHistory();
		komb2.clearHistory();
		komb3.clearHistory();
		OEread.clearHistory();
		OEwrite.clearHistory();

		komb1.setActive(Boolean.parseBoolean(states[0]));
		komb2.setActive(Boolean.parseBoolean(states[1]));
		komb3.setActive(Boolean.parseBoolean(states[2]));
		OEread.setActive(Boolean.parseBoolean(states[3]));
		OEwrite.setActive(Boolean.parseBoolean(states[4]));

		ffc = new FlipFlopD(komb1, new Signal(Boolean.parseBoolean(states[5])));
		Dffc = new FlipFlopD(ffc.getOut(), new Signal(
				Boolean.parseBoolean(states[6])));
		rd = new FlipFlopRS(ffc.getOut(), komb2, new Signal(
				Boolean.parseBoolean(states[7])));
		Drd = new FlipFlopRS(enFFC, rd.getOut(), new Signal(
				Boolean.parseBoolean(states[8])));
		wr = new FlipFlopRS(ffc.getOut(), komb3, new Signal(
				Boolean.parseBoolean(states[9])));
		Dwr = new FlipFlopRS(enFFC, wr.getOut(), new Signal(
				Boolean.parseBoolean(states[10])));

		clkFstPhase = Boolean.parseBoolean(states[11]);
	}

	public String createRestorePoint() {
		String res = beginTag() + "\n\t" + komb1.isActive() + " "
				+ komb2.isActive() + " " + komb3.isActive();
		res += " " + OEread.isActive() + " " + OEwrite.isActive() + " "
				+ ffc.getOut().isActive() + " " + Dffc.getOut().isActive();
		res += " " + rd.getOut().isActive() + " " + Drd.getOut().isActive()
				+ " " + wr.getOut().isActive() + " " + Dwr.getOut().isActive();
		res += " " + clkFstPhase + "\n" + endTag();
		return res;
	}

	public String beginTag() {
		return "<SyncBlock>";
	}

	public String endTag() {
		return "</SyncBlock>";
	}

	public void previousClk() {
		clkFstPhase = true;

		komb1.previousClk();
		komb2.previousClk();
		komb3.previousClk();
		OEread.previousClk();
		OEwrite.previousClk();

		ffc.previousClk();
		Dffc.previousClk();
		rd.previousClk();
		Drd.previousClk();
		wr.previousClk();
		Dwr.previousClk();

		sysbus.previousClk();
	}

}
